package org.report4service.utils;

import java.util.Calendar;
import java.util.Date;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;

/**
 * Constructs the calendar
 * 
 * @author Jmpango
 * 
 */
public class CalenderUtil {

    private static final String[] MONTH_LABELS = new String[]{
        "January", "February", "March", "April", "May", "June", "July", "August", "September",
        "October", "November", "December"
    };
    private static final String[] WEEKDAY_LABELS = new String[]{
        "M", "T", "W", "Th", "F", "S", "Su"
    };
    /** cell padding */
    private static final short cellPadding = 1;
    private static final short DOUBLE_CELL_PADDING = 2 * cellPadding;
    /** starting week day: 0 for Monday, 6 for Sunday */
    private final short startWeekday = 0;
    /** cells border properties */
    private static final int borderWidth = 1;
    private static final int borderColor = 0x4080c1;
    /** weekday labels properties */
    private Font weekDayFont = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD,
            Font.SIZE_MEDIUM);
    private int weekdayBgColor = 0x4080c1;
    private int weekdayColor = 0xffffff;
    private int weekHeight = 0;
    /** header (month-year label) properties */
    private Font headerFont = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_LARGE);
    private int headerColor = 0x000000;
    private int headerHeight = 0;
    /** cells properties */
    private int foreColor = 0x000000;
    private int bgColor = 0xffffff;
    private int selectedBgColor = 0xccccff;
    private int selectedForeColor = 0x000000;
    private int cellWidth = 0;
    private int cellHeight = 0;
    /** time properties */
    private long currentTimestamp = 0;
    private Calendar calendar = null;
    private int weeks = 0;
    /** table properties */
    private int tableWidth = 0;
    private int tableHeight = 0;
    /** default screen measurements */
    private int getWidth = 0;
    private int getHeight = 0;
    private final String todayDate;
    /** property to show current date */
    public int weeksTotalHeight = 1;

    public CalenderUtil(Date date, int getWidth, int getHeight) {
        calendar = Calendar.getInstance();
        setDate(date);
        todayDate = calendar.get(Calendar.DAY_OF_MONTH) + " " + MONTH_LABELS[calendar.get(Calendar.MONTH)] + " " + calendar.get(Calendar.YEAR);

        this.getWidth = getWidth;
        this.getHeight = getHeight;
        intializeCalender();
    }

    /**
     * Initializes the calender
     */
    public void intializeCalender() {

        this.cellWidth = (getWidth - 15) / 7 + 2 * cellPadding;
        this.cellHeight = getHeight / 11 + 2 * cellPadding;

        this.headerHeight = headerFont.stringWidth("MM") + DOUBLE_CELL_PADDING;
        this.weekHeight = weekDayFont.getHeight() + DOUBLE_CELL_PADDING;

        this.tableWidth = (short) (7 * (cellWidth + borderWidth) + borderWidth);
        initHeight();
    }

    /**
     * Calculate the height of a cell
     */
    private void initHeight() {
        this.tableHeight = headerHeight + weekHeight +
                this.weeks * (cellHeight + borderWidth) + borderWidth;
    }

    public Date getSelectedDate() {
        return calendar.getTime();
    }

    public int getSelectedDay() {
        return calendar.get(Calendar.DAY_OF_MONTH);
    }

    public int getSelectedMonth() {
        return calendar.get(Calendar.MONTH) + 1;
    }

    public int getSelectedYear() {
        return calendar.get(Calendar.YEAR);
    }

    /**
     * Sets the time
     * 
     * @param date
     */
    private void setDate(Date date) {
        currentTimestamp = date.getTime();

        calendar.setTime(date);

        // weeks number can change, depending on week starting day and month
        // total days
        this.weeks = (int) Math.ceil(((double) getStartWeekday() + getMonthDays()) / 7);
    }

    private void setDate(long timestamp) {
        setDate(new Date(timestamp));
    }

    /**
     * Gets the number of days in a month
     * 
     * @return number of days
     */
    private int getMonthDays() {
        int month = calendar.get(Calendar.MONTH);

        switch (month) {
            case 3:
            case 5:
            case 8:
            case 10:
                return 30;
            case 1:
                return calendar.get(Calendar.YEAR) % 4 == 0 && calendar.get(Calendar.YEAR) % 100 != 0 ? 29
                        : 28;
            default:
                return 31;
        }
    }

    /**
     * Gets day that starts a week
     * 
     * @return day
     */
    private int getStartWeekday() {
        /* let's create a new calendar with same month and year, but with day 1 */
        Calendar c = Calendar.getInstance();

        c.set(Calendar.MONTH, calendar.get(Calendar.MONTH));
        c.set(Calendar.YEAR, calendar.get(Calendar.YEAR));
        c.set(Calendar.DAY_OF_MONTH, 1);

        /* we must normalize DAY_OF_WEEK returned value */
        return (c.get(Calendar.DAY_OF_WEEK) + 5) % 7;
    }

    public void keyPressed(int keyCode) {
        switch (keyCode) {
            case Canvas.UP:
                go(-7);
                break;
            case Canvas.DOWN:
                go(7);
                break;
            case Canvas.RIGHT:
                go(1);
                break;
            case Canvas.LEFT:
                go(-1);
                break;
        }
    }

    /**
     * @param i
     */
    private void go(int delta) {
        int prevMonth = calendar.get(Calendar.MONTH);

        setDate(currentTimestamp + 86400000 * delta);

        /*
         * we have to check if month has changed if yes, we have to recalculate
         * month height since weeks number could be changed
         */
        if (calendar.get(Calendar.MONTH) != prevMonth) {
            initHeight();
        }
    }

    public void paint(Graphics g) {

        // painting background
        g.setColor(bgColor);
        g.fillRect(0, 0, tableWidth, tableHeight);

        // painting header (month-year label)
        g.setFont(headerFont);
        g.setColor(headerColor);
        g.drawString(calendar.get(Calendar.DAY_OF_MONTH) + " " + MONTH_LABELS[calendar.get(Calendar.MONTH)] + " " + calendar.get(Calendar.YEAR),
                tableWidth / 2, cellPadding, Graphics.TOP | Graphics.HCENTER);

        // painting week days labels
        g.translate(0, headerHeight);

        g.setColor(weekdayBgColor);
        g.fillRect(0, 0, tableWidth, weekHeight);

        g.setColor(weekdayColor);
        g.setFont(weekDayFont);

        for (int i = 0; i < 7; i++) {
            g.drawString(WEEKDAY_LABELS[(i + startWeekday) % 7],
                    borderWidth + i * (cellWidth + borderWidth) + cellWidth / 2,
                    cellPadding,
                    Graphics.TOP | Graphics.HCENTER);
        }

        // painting cells borders
        g.translate(0, weekHeight);

        g.setColor(borderColor);

        for (int i = 0; i <= weeks; i++) {
            weeksTotalHeight = i;
            g.fillRect(0, i * (cellHeight + borderWidth), tableWidth, borderWidth);
        }

        for (int i = 0; i <= 7; i++) {
            g.fillRect(i * (cellWidth + borderWidth), 0, borderWidth, tableHeight - headerHeight - weekHeight);
        }

        // painting days
        int days = getMonthDays();//31
        int dayIndex = (getStartWeekday() - this.startWeekday + 7) % 7;

        g.setColor(foreColor);

        int currentDay = calendar.get(Calendar.DAY_OF_MONTH);

        for (int i = 0; i < days; i++) {
            int weekday = (dayIndex + i) % 7;
            int row = (dayIndex + i) / 7;

            int x = borderWidth + weekday * (cellWidth + borderWidth) + cellWidth / 2;
            int y = borderWidth + row * (cellHeight + borderWidth) + cellPadding;

            // if this is the current day, we'll use selected bg and fore colors
            if (i + 1 == currentDay) {
                g.setColor(selectedBgColor);
                g.fillRect(
                        borderWidth + weekday * (cellWidth + borderWidth),
                        borderWidth + row * (cellHeight + borderWidth),
                        cellWidth, cellHeight);
                g.setColor(selectedForeColor);
            }

            g.drawString("" + (i + 1), x, y, Graphics.TOP | Graphics.HCENTER);

            // if this is the current day, we must restore standard fore color
            if (i + 1 == currentDay) {
                g.setColor(foreColor);
            }
        }

        /*g.setColor(bgColor);
        g.fillRect(0, (weeksTotalHeight * 28) + 10, tableWidth, headerHeight);
        g.setFont(weekDayFont);
        g.setColor(headerColor);
        g.drawString("Today's date:  " + todayDate, tableWidth / 2, (weeksTotalHeight * 28) + 10,
        Graphics.TOP | Graphics.HCENTER);
        g.translate(0, headerHeight);*/

        // let's traslate back!
        g.translate(0, -headerHeight - weekHeight);

    }
}
